package com.ap.core.storedprocedure.impl;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;

import com.ap.core.common.tuple.ThreeTuple;
import com.ap.core.common.types.ParameterType;
import com.ap.core.storedprocedure.api.BaseStoredProcedure;
import com.ap.core.storedprocedure.api.QueryCallback;
import com.ap.core.storedprocedure.api.StoredProcedureInfo;
import oracle.jdbc.OracleTypes;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;

/**
 * Utility class for querying using stored procedures.
 */
public class QueryProcedure extends BaseStoredProcedure {
    
	    
    /**
     * Initialises the call string and in parameters.
     * 
     * @param storedProcedureInfo Stored procedure info.
     */
    public QueryProcedure(StoredProcedureInfo storedProcedureInfo) {
        super(storedProcedureInfo);
    }

    /**
     * Executes the query stored procedure using the entity manager.
     *
     * @param entityManager entityManager to use for getting connection.
     * @param queryCallback Query callback.
     */
    public void execute(final EntityManager entityManager, final QueryCallback queryCallback) {
        final Session session = (Session) entityManager.getDelegate();

        session.doWork(new Work() {
            @Override
            public void execute(Connection connection) throws SQLException {
                CallableStatement cs = null;
                // This is to close the result sets after processing in finally block
                List<ResultSet> resultSets = new ArrayList<ResultSet>();
                List<Object> results = new ArrayList<Object>();
                try{
                    cs = executeQueryStoredProcedure(connection, cs, resultSets, queryCallback);

                    int index = 0;
                    for (ThreeTuple<Integer, ?, ParameterType> inParam : storedProcedureInfo.getInParams()) {
                        index++;
                        if(ParameterType.OUT == inParam.third){
                            Object obj = cs.getObject(index);
                            results.add(obj);
                            if(OracleTypes.CURSOR == inParam.first){
                                resultSets.add((ResultSet)obj);
                            }
                        }
                    }

                    queryCallback.onResults(results);
                }
                catch (SQLException e) {
                    throw new RuntimeException("Error executing stored procedure " + storedProcedureInfo.getCallString(), e);
                } finally {
                    closeResources(cs, resultSets);
                }
            }

        });
    }

    private CallableStatement executeQueryStoredProcedure(Connection connection, CallableStatement cs, List<ResultSet> resultSets, final QueryCallback queryCallback) throws SQLException {
        cs = connection.prepareCall(storedProcedureInfo.getCallString());
        setInOutVariables(cs);
        cs.executeUpdate();
        return cs;
    }



}
